#include <kuai/misc/thread.hpp>

#include <kuai/mol/molio.hpp>
#include <kuai/sim/simjob.hpp>
#include <kuai/sim/energy.hpp>


namespace kuai {

	SimulationJob::SimulationJob() {
		cpu = 1;
		random_seed = int(::time(NULL));
		mutex = NULL;
		barrier = NULL;
		group = NULL;
	}
	SimulationJob::~SimulationJob() {
		assert (mutex == NULL);
		assert (barrier == NULL);
		assert (group == NULL);
	}

#ifndef USE_DUMN_THREAD
	void SimulationJob::setCPU(size_t i) {
		if (i == 0) {
			cpu = hardware_concurrency();
		}
		else if (i > 0) {
			cpu = i;
		}
	}
#endif

	namespace {
		struct SimulationJob_AddE 
			: public SimulationEnvironment
		{
			void operator()() {
				set->start(*this);
				set->addE(*this, e);
				set->finish(*this);
			}

			void finalize() {
				set->finalize(*this);
			}

			RealNumber* e;
			EnergySet* set;
		};

		struct SimulationJob_AddEG
			: public SimulationEnvironment
		{
			void operator()() {
				set->start(*this);
				set->addEG(*this, e, g);
				set->finish(*this);
			}

			void finalize() {
				set->finalize(*this);
			}

			RealNumber* e;
			XYZ*		g;
			EnergySet* set;
		};

		struct SimulationJob_AddEGH
			: public SimulationEnvironment
		{
			void operator()() {
				set->start(*this);
				set->addEGH(*this, e, g, h);
				set->finish(*this);
			}

			void finalize() {
				set->finalize(*this);
			}

			RealNumber* e;
			XYZ*		g;
			RealNumber** h;
			EnergySet* set;
		};
		struct SimulationJob_AddHX
			: public SimulationEnvironment
		{
			void operator()() {
				set->start(*this);
				set->addHX(*this, x, result);
				set->finish(*this);
			}

			void finalize() {
				set->finalize(*this);
			}

			const RealNumber* x;
			RealNumber* result;
			EnergySet* set;
		};

	}
	

	void SimulationJob::addE(EnergySet& efunc, RealNumber e[]) {
		std::vector<SimulationJob_AddE> env(getCPU());
		for (size_t i = 0; i < cpu; ++i) {
			env[i].e = e;
			env[i].set = &efunc;
		}
		runjob(env);
	}
	

	void SimulationJob::addEG(EnergySet& efunc, RealNumber e[], XYZ g[]) {
		std::vector<SimulationJob_AddEG> env(getCPU());
		for (size_t i = 0; i < cpu; ++i) {
			env[i].e = e;
			env[i].g = g;
			env[i].set = &efunc;
		}
		runjob(env);
	}

	void SimulationJob::addEGH(EnergySet& efunc, RealNumber e[], XYZ g[], RealNumber* hess[]) {
		std::vector<SimulationJob_AddEGH> env(getCPU());
		for (size_t i = 0; i < cpu; ++i) {
			env[i].e = e;
			env[i].g = g;
			env[i].h = hess;
			env[i].set = &efunc;
		}
		runjob(env);
	}

	void SimulationJob::addHX(EnergySet& efunc, const RealNumber x[], RealNumber result[]) {
		std::vector<SimulationJob_AddHX> env(getCPU());
		for (size_t i = 0; i < cpu; ++i) {
			env[i].x = x;
			env[i].result;
			env[i].set = &efunc;
		}
		runjob(env);
	}

	
	void SimulationJob::run(EnergySet& efunc, ControlSet& controls, size_t nsteps) {
	
	}


}

